use clap::Parser;
use item::{Code, Env};
use lexer::TokenSeq;
use parser::{Error, Parser as MakeCParser};

mod c;
mod char_seq;
mod item;
mod lexer;
mod parser;

type Result<T> = std::result::Result<T, ()>;

#[derive(Parser)]
struct CommandLine {
    mkc: String,
    /// Generate #line FILE LINE
    #[clap(short, long)]
    gen_line: bool,
}

fn runtime() -> Result<()> {
    env_logger::init_from_env("MAKEC_LOG");

    let cli = CommandLine::parse();

    let code = std::fs::read_to_string(&cli.mkc)
        .map_err(|err| eprintln!("makec: can't load {mkc}: {err}", mkc = cli.mkc))?;

    let lexer = TokenSeq::new(code.chars(), cli.mkc.clone().into());
    let mut parser = MakeCParser::new(lexer);

    let mut stats = vec![];
    loop {
        let stat_pos = match parser.parse_stat() {
            Ok(stat_pos) => stat_pos,
            Err(Error::Eof) => {
                break;
            }
            Err(err) => {
                eprintln!("{err}");
                return Err(());
            }
        };

        stats.push(stat_pos);
    }

    let mut code = Code::new(stats);

    let mut env = Env::new();

    code.bind_globals(&mut env).map_err(|err| {
        eprintln!("{err}");
    })?;

    code.infer_type(&env).map_err(|err| {
        eprintln!("{err}");
    })?;

    code.show(cli.gen_line);

    Ok(())
}

fn main() {
    if runtime().is_err() {
        eprintln!("makec: failed to compile to C");
        std::process::exit(1)
    }
}
